package nodes;

import java.util.Map;

import compiler.Compiler;
import compiler.SymbolTable;

import descriptors.AbstractDescr;
import descriptors.ArrayDescr;
import descriptors.ConstDescr;
import descriptors.RecordDescr;
import descriptors.VarDescr;

public class OperatorNode extends ExpressionNode {

	private String operator;
	private AbstractNode left;
	private AbstractNode right;
	
	public OperatorNode(String operator,AbstractNode left,AbstractNode right,int line,int column){
		super(line,column);
		this.operator=operator;
		this.left=left;
		this.right=right;
	}
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public AbstractDescr compile(
			SymbolTable symbolTable, Compiler compiler) {
		AbstractDescr result = null, leftDescr=null;
		leftDescr=left.compile(symbolTable, compiler);
		if (leftDescr instanceof ConstDescr) {
			compiler.write("PUSHI, " + ((ConstDescr) leftDescr).value());
			}
		if (! (right instanceof IdentNode)) {
			AbstractDescr rightDescr=right.compile(symbolTable, compiler);
			if (rightDescr instanceof ConstDescr) {
				compiler.write("PUSHI, " + ((ConstDescr) rightDescr).value());
			}
		}

		if(operator.equals("+")){
			compiler.write("ADD");
		}
		else if(operator.equals("-")){
			compiler.write("SUB");
		}
		else if(operator.equals("/")){
			compiler.write("DIV");
		}
		else if(operator.equals("*")){
			compiler.write("MUL");
		}
		else if(operator.equals("<=")){
			compiler.write("LE");
		}
		else if(operator.equals("<")){
			compiler.write("LT");
		}
		else if(operator.equals("=")){
			compiler.write("EQ");
		}
		else if(operator.equals(">=")){
			compiler.write("GE");
		}
		else if(operator.equals(">")){
			compiler.write("GT");
		}
		else if(operator.equals("#")){
			compiler.write("NEQ");
		}
		else if(operator.equals("[")){
			// basic idea: get the size of the base type on the stack to multiply it with the result of compiling right node
			if(left instanceof IdentNode){ //nothing was compiled on the left so far
				VarDescr leftVarDescr=(VarDescr)symbolTable.get(compiler.level(), ((IdentNode)left).ident());
				ArrayDescr leftArrayDescr=(ArrayDescr)leftVarDescr.type();
				compiler.write("PUSHI, "+leftArrayDescr.baseType().getSize());
			}
			else{
				if(leftDescr instanceof ArrayDescr){
					compiler.write("PUSHI,"+((ArrayDescr)leftDescr).baseType().getSize());
				}
				else{ // left is VarDescr...
					compiler.write("PUSHI,"+((ArrayDescr)((VarDescr)leftDescr).type()).baseType().getSize());
				}
			}
			// Something needs to be returned 
			if (leftDescr instanceof ArrayDescr){	
				result = ((ArrayDescr)leftDescr).baseType();
			} else { // left is VarDescr...
				result = ((ArrayDescr)((VarDescr)leftDescr).type()).baseType();
			}
			compiler.write("MUL");
			compiler.write("ADD");
		}
		else if(operator.equals(".")){
			// The base address of the record should already be on the stack, we now add the relative address
			Map<String, AbstractDescr> recSymbolTable = null;
			if (left instanceof IdentNode){
				recSymbolTable= ((RecordDescr) ((VarDescr) symbolTable.get(compiler.level(),((IdentNode) left).ident())).type()).recSymbolTable();
			} else {
				AbstractDescr recDescr = null;
				if (leftDescr instanceof RecordDescr) {
						recDescr = leftDescr;
				} else {
					recDescr = ((VarDescr)leftDescr).type();
				}
				recSymbolTable = ((RecordDescr)recDescr).recSymbolTable();
			}
			result = recSymbolTable.get(((IdentNode)right).ident());
			compiler.write("PUSHI, "+((VarDescr)result).address()); // put relative address on the stack
			compiler.write("ADD");
		}
		return result;
	}

	@Override
	public void print() {
		trace("OperatorNode: "+operator);
		left.print();
		right.print();
		unindent();
	}

}
